using System;
using System.Web;
using System.Text.RegularExpressions;
using System.Configuration;
using System.Collections.Specialized;
using System.IO;

using Azke.Web.Util;
using Azke.Core.Util;

namespace Azke.Web.HttpModules
{
	/// <summary>
	/// The default url handler.
	/// </summary>
	public class UrlHandlerModule : IHttpModule
	{
        private LogManager _logger = LogManager.GetLogger();

		public UrlHandlerModule()
		{
		}

		#region IHttpModule Members

        public void Init(HttpApplication application)
		{
            application.AuthorizeRequest += new EventHandler(application_AuthorizeRequest);
            application.EndRequest += new EventHandler(application_EndRequest);
            application.PreRequestHandlerExecute += new EventHandler(application_PreRequestHandlerExecute);
		}

		public void Dispose()
		{
		}

		#endregion

		/// <summary>
		/// Read the match patterns from the configuration and try to rewrite the url.
		/// TODO: caching? 
		/// </summary>
		/// <param name="urlToRewrite"></param>
		/// <param name="context"></param>
		private void RewriteUrl(string urlToRewrite, HttpContext context)
		{
			NameValueCollection mappings = (NameValueCollection)ConfigurationManager.GetSection("UrlMappings");
			for (int i = 0; i < mappings.Count; i++)
			{
				string matchExpression = UrlHelper.GetApplicationPath() + mappings.GetKey(i);
				Regex regEx = new Regex(matchExpression, RegexOptions.IgnoreCase|RegexOptions.Singleline|RegexOptions.CultureInvariant|RegexOptions.Compiled);
				if (regEx.IsMatch(urlToRewrite))
				{
					// Don't rewrite when the mapping is an empty string (used for Default, Admin etc.)
					if (mappings[i] != String.Empty)
					{
						// First check if the Application is upgrading. If so, redirect everything to the
						// maintenance page.
						if (Convert.ToBoolean(HttpContext.Current.Application["IsUpgrading"]))
						{
							HttpContext.Current.Response.Redirect("~/Install/Sorry.aspx", true);
							return;
						}

						// Store the original url in the Context.Items collection. We need to save this for setting
						// the action of the form.
						string rewritePath = regEx.Replace(urlToRewrite, UrlHelper.GetApplicationPath() + mappings[i]);
						// MONO_WORKAROUND: split the rewritten path in path, pathinfo and querystring
						// because MONO doesn't handle the pathinfo directly
						//context.RewritePath(rewritePath);
						string querystring = String.Empty;
						string pathInfo = String.Empty;
						string path = String.Empty;
						// 1. extract querystring
						int qmark = rewritePath.IndexOf("?");
						if (qmark != -1 || qmark + 1 < rewritePath.Length) 
						{
							querystring = rewritePath.Substring (qmark + 1);
							rewritePath = rewritePath.Substring (0, qmark);
						} 
						// 2. extract pathinfo
						int pathInfoSlashPos = rewritePath.IndexOf("aspx/") + 4;
						if (pathInfoSlashPos > 3)
						{
							pathInfo = rewritePath.Substring(pathInfoSlashPos);
							rewritePath = rewritePath.Substring(0, pathInfoSlashPos);
						}
						// 3. path
						path = rewritePath;
                        _logger.Write(LogLevel.DEBUG, this.ToString(), "urlToRewrite = " + urlToRewrite);
                        _logger.Write(LogLevel.DEBUG, this.ToString(), "path = " + path);
                        _logger.Write(LogLevel.DEBUG, this.ToString(), "pathInfo = " + pathInfo);
                        _logger.Write(LogLevel.DEBUG, this.ToString(), "querystring = " + querystring);
						
						context.RewritePath(path, pathInfo, querystring);
                        context.Items["VirtualUrl"] = urlToRewrite;
					}
					break;
				}
                
			}
		}

        private void application_AuthorizeRequest(object sender, EventArgs e)
        {
            HttpApplication app = (sender as HttpApplication);
            if (app != null)
            {
                // register start time for performance measurements.
                app.Context.Items["starttime"] = DateTime.Now;
                _logger.Write(LogLevel.INFO, this.ToString(), "Starting request: " + app.Request.RawUrl);
                string url = HttpContext.Current.Request.RawUrl;
                RewriteUrl(url, app.Context);
            }
        }

        private void application_EndRequest(object sender, EventArgs e)
		{
			HttpApplication app = (sender as HttpApplication);
            if (app != null)
            {
                // Ready, write the execution time to the debug output.
                if (app.Context.Items.Contains("starttime"))
                {
                    TimeSpan ts = DateTime.Now - (DateTime)HttpContext.Current.Items["starttime"];
                    _logger.Write(LogLevel.INFO, this.ToString(), "Total execution time : " + ts.Milliseconds.ToString() + " ms.");
                    _logger.Write(LogLevel.INFO, this.ToString(), "Ending request: " + app.Request.RawUrl);
                }
            }
		}

        private void application_PreRequestHandlerExecute(object sender, EventArgs e)
        {
            HttpApplication application = sender as HttpApplication;
            System.Web.UI.Page page = application.Context.CurrentHandler as System.Web.UI.Page;
            if (page != null)
            {
                page.PreInit += new EventHandler(application_PagePreInit);
            }
        }

        static private void application_PagePreInit(object sender, EventArgs e)
        {
            HttpContext context = HttpContext.Current;

            if (context.Items.Contains("VirtualUrl"))
            {
                string virtualUrl = context.Items["VirtualUrl"].ToString();
                // rewrite back to request Url
                context.RewritePath(virtualUrl, "", "");
            }
        }
	}

	/// <summary>
	/// ConfigSection class
	/// </summary>
	public class UrlMappingsSectionHandler : NameValueSectionHandler
	{
		protected override string KeyAttributeName
		{
			get { return "match"; }
		}

		protected override string ValueAttributeName
		{
			get { return "replace"; }
		}
	}
}
